
#include "pty_shell.hpp"

#include <termios.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/telnet.h>    /* for telnet opt */
#include <pty.h>

#include <pthread.h>
#include <signal.h>

pty_map pty_manager::_map;

void pty_manager::add(pid_t pid, pty_shell_ptr p) 
{
    _map[pid] = p;
};

void pty_manager::remove(pid_t pid)
{
    pty_map::iterator iter = _map.find(pid);
    if (iter == _map.end()) return;
    (*iter).second->set_pid(0);
    PDBG("pty(pid=%d), is exit\n", pid);
    _map.erase(iter);
};


void pty_shell::close_pty()
{
    if (!_shell_pid) return ;
    if (!_ptyfd) return ;
    close(_ptyfd);
    _ptyfd = _shell_pid = 0;
};

void pty_shell::set_pid(pid_t pid)
{
    PDBG(">>>>>>>>>>>>>  close pty <<<<<<<<<<<<<\n");
    if (pid == 0) close_pty();
    _shell_pid = pid;
};

int pty_shell::open_pty(const string_vector& cmdvec)
{
    int slave;
    char tty_name[2048];
    char *argv[200];
    
    if (_shell_pid != 0) return -1;

	struct termios termbuf;
    if (openpty(&_ptyfd, &slave, NULL, NULL, NULL)) 
        throw std::runtime_error("openpty is failed!!");
    if (ttyname_r(slave, tty_name, 2048) != 0) 
        throw std::runtime_error("openpty is failed!!");

    PDBG("ptyfd: %d ttyname: %s\n", _ptyfd, tty_name);
    
    //_ptystream.reset( new posix::stream_descriptor(_io_service, (ptyfd)));

    pid_t pid = fork(); /* NOMMU-friendly */
    if (pid < 0) {
        close_pty();
        throw std::runtime_error("fork is failed!!");
	}
	if (pid > 0) {
        pty_manager::add(pid, shared_from_this());
		_shell_pid = pid;
        PDBG("_shell_pid : %d\n", _shell_pid);
		return _ptyfd;
	}
    usleep(5000);   /* XXX: for sink */

    /* make new session and process group */
    setsid();

    sigset_t new_mask;
    sigfillset(&new_mask);
    sigset_t old_mask;
    pthread_sigmask(SIG_UNBLOCK, &new_mask, &old_mask);

    for (int idx=0; idx<25; idx++) /* FIXME */
        close(idx);
    //close(0);
    open(tty_name, O_RDWR);
    dup2(0, 1);
    dup2(0, 2);
	tcsetpgrp(0, getpid()); /* switch this tty's process group to us */

	/* The pseudo-terminal allocated to the client is configured to operate in
	 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
	tcgetattr(0, &termbuf);
	termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
	termbuf.c_oflag |= ONLCR | XTABS;
	termbuf.c_iflag |= ICRNL;
	termbuf.c_iflag &= ~IXOFF;
	/*termbuf.c_lflag &= ~ICANON;*/
    tcsetattr(STDIN_FILENO, TCSANOW, &termbuf);

    int idx=0;
    for (; idx < cmdvec.size(); idx++) {
        argv[idx] =  (char*)cmdvec[idx].c_str();
    }
    argv[idx] = NULL;
    //execlp("/bin/bash",  "/bin/bash", NULL);
    execvp(argv[0], argv);
//    execlp("ls", "ls", "-l", NULL);
    perror("fail: ");
    exit(1);
}

